home *** CD-ROM | disk | FTP | other *** search
- INTRODUCCION AL ASM: USO DE LOS PROGRAMAS ENSAMBLADORES (II)
- ============================================================
-
- Continuamos en este octavo capítulo del curso la explicación del HELLO.ASM
- en el punto donde la dejamos al final del anterior capítulo.
-
- El último segmento del programa es el segmento de código, al que le hemos
- dado el nombre 'CODIGO'. La primera línea de este segmento es un ASSUME, cuya
- función vamos a ver detalladamente.
-
- Cuando vimos los distintos modos de direccionamiento, vimos que cada uno
- usaba un registro segmento por defecto (por cierto, uno de los registros de
- segmento estaba equivocado: el modo [BP+xx] usa el segmento SS por defecto, y
- no el DS como aparecía). Cuando queremos que una instrucción que opera por
- defecto con un segmento opere con otro, es necesario anteponer al operando que
- referencia a memoria el nombre del segmento seguido de dos puntos. Por ejemplo,
- veamos la siguiente instrucción, que accede al segmento apuntado por DS:
-
- mov ax,[0]
-
- Si queremos que cargue la primera palabra del segmento apuntado por CS:
-
- mov ax,cs:[0]
-
- A nivel de código de máquina, lo que se hace es prefijar la instrucción an-
- terior con un byte (existen cuatro posibles valores, uno para cada segmento)
- que especifica que no se debe usar el segmento por defecto. A este prefijo se
- le suele denominar 'segment override'.
-
- Cuando en un listado en lenguaje ensamblador se genera un acceso a memoria,
- el ensamblador es capaz de meter automáticamente 'segment override's donde sea
- necesario (además de permitirnos especificarlos a nosotros). Para ello, hay que
- decirle al ensamblador a cual de los segmentos que hemos definido en el listado
- está apuntando cada uno de los registros de segmento. Así, cuando generamos una
- referencia a una etiqueta, comprueba en qué segmento ha sido definida y busca
- cuál de los registros de segmento apunta a ése segmento. Así, puede decidir si
- es necesario generar un 'segment override', y cuál de ellos es necesario. la
- directiva 'ASSUME' dice al ensamblador a qué segmento apunta cada registro de
- segmento, como vemos en HELLO.ASM. De todas maneras, somos nosotros los encar-
- gados de hacer que, efectivamente, los registros de segmento apunten a los seg-
- mentos especificados.
-
- En caso de que el ensamblador encuentre un acceso a una etiqueta definida en
- un segmento no mencionado en el ASSUME, se quejará con un error del tipo
- 'unreachable data' ('datos inaccesibles').
-
- Si un segmento no va a estar apuntando a ninguno de los segmentos definidos
- en el listado (por ejemplo, si apunta a la memoria de vídeo), se puede añadir
- un ASSUME con nombre de segmento 'NOTHING', de manera que el ensamblador no
- asuma que el registro de segmento en cuestión permite acceder a ninguna varia-
- ble conocida. Al principio del proceso de ensamblado, el ensamblador está como
- si hubiera leído una línea así:
-
- ASSUME CS:NOTHING, DS:NOTHING, ES:NOTHING, SS:NOTHING
-
- Es importante comprender el funcionamiento de la directiva ASSUME, ya que
- puede causar confusiones en algunos casos.
-
- Dentro de un segmento de código, las instrucciones incluidas pueden estar
- agrupadas en bloques lógicos, llamados 'procedimientos'. Estos funcionan a modo
- de las funciones de C o los procedimientos y funciones de Pascal. Aunque las
- últimas versiones de los ensambladores permiten dar listas de argumentos, va-
- riables locales, etc... (generando el código oportuno para manejarlos), no en-
- traremos en ello. Para nosotros no será más que una manera de agrupar el código
- en bloques lógicos, y de identificar estos bloques mediante un nombre. El nom-
- bre de un procedimiento entra en la tabla de símbolos, y se le asocia la di-
- rección de la primera instrucción después de la directiva 'PROC'.
-
- En el programa HELLO, tenemos un único PROC, al que llamamos 'Entrada'.
- Veamos las líneas detalladamente:
-
- * mov ax,DATOS
-
- La etiqueta DATOS está en la tabla de símbolos, asociada al segmento de este
- nombre. El ensamblador genera en el '.OBJ' una referencia, que irá después en
- el '.EXE', de manera que esta instrucción cargue en el registro AX el valor de
- segmento necesario para que 'ax:0' apunte al primer byte del segmento datos.
- Este valor variará de una vez que se cargue el programa a otra, pero el carga-
- dor del DOS hace que siempre se asigne el valor correcto a AX.
-
- * mov ds,ax
-
- Hace que DS, efectivamente, apunte al segmento de código del programa. Así,
- podremos acceder a la cadena contenida en este segmento.
-
- * mov dx, OFFSET Msg
-
- Carga DX con el offset de la etiqueta Msg dentro de su segmento. Ya que Msg
- está al principio del segmento, dx se cargará con el valor 0. Aunque el progra-
- ma pueda cargarse en cualquier punto de la memoria, el offset de algún dato o
- alguna instrucción dentro de su segmento será siempre el mismo, por lo que esta
- instrucción no se incluye en la tabla de reubicación del '.EXE'.
-
- * mov ah,9
- * int 21h
-
- Se invoca el servicio 9 de la interrupción 21h del DOS. Este servicio escri-
- en la pantalla la cadena apuntada por DS:DX (que en este caso será la cadena
- Msg). Se retorna al encontrar el carácter '$', que no se imprime. Podemos apre-
- ciar que en el segmento DATOS aparece el símbolo '$' dentro de la propia
- cadena.
-
- * mov ax,4C00h
- * int 21h
-
- Se invoca el servicio 4Ch de la interrupción 21h. Este servicio retorna al
- programa que invocó al programa actual (normalmente al COMMAND.COM), con un
- nivel de error ('ERRORLEVEL') especificado en el registro AL. En este caso, se
- retorna el valor 0 (que habitualmente indica ausencia de errores).
-
- Después de cerrar el procedimiento 'Entrada' y el segmento 'CODIGO', encon-
- tramos una última directiva, 'END', con el nombre del procedimiento como pará-
- metro. Esta directiva puede llevar un único parámetro, opcional, que indica el
- punto de entrada al programa. En caso de linkar varios '.OBJ' para generar un
- ejecutable, sólo uno de ellos puede especificar punto de entrada (pero necesa-
- riamente alguno debe especificarlo), por lo que el resto de los módulos termi-
- nan su listado con un simple 'END'. En este caso, ya que 'Entrada' se asocia a
- la primera instrucción del procedimiento, el punto de entrada es la instrucción
- 'mov ax,DATOS'.
-
- Antes de pasar a ver las directivas simplificadas de segmento, veamos un
- punto importante de los ensambladores. Los ensambladores comerciales llevan
- incorporado un evaluador de expresiones, de manera que en cualquier lugar donde
- el ensamblador espere una constante puede aparecer una expresión que pueda
- evaluarse en tiempo de ensamblado. Estas expresiones pueden llevar operadores;
- podremos encontrar una lista de todos en el manual del ensamblador o en la ayu-
- da online. Los más importantes son los siguientes:
-
- +,-,*,/,MOD Aritméticos
- OR, AND, XOR, NOT Lógicos
- SEG, OFFSET Aplicados a una etiqueta, dan el valor de segmento donde
- reside (se incluye una entrada en la tabla de relocaliza-
- ción) y el offset dentro de su segmento
- SHL, SHR Sintáxis: X SHL Y. Devuelve el valor X desplazado a la
- izquierda (derecha) Y bits.
- () Se utilizan para saltarse la precedencia de operadores
-
- Líneas válidas utilizando estos operadores son las siguientes (suponiendo
- que todas las etiquetas hayan sido definidas previamente):
-
- Msg DW 55h SHR 2
- Variable DB 15 + OFFSET Msg
- mov ax,4+3*OFFSET Msg
- mov dx,2 + NOT 01010101b
- add ax,es:[bx+di+1+OFFSET Msg]
- sub si,(2Ah+13d)*2
-
- Son todas válidas porque las expresiones que aparecen puede evaluarlas el
- propio ensamblador para obtener valores numéricos. Las siguientes instruccio-
- nes, en cambio, aunque perfectamente inteligibles, son ilegales y el ensambla-
- dor se quejará si las incluímos:
-
- mov ax,2+bx
- add dx,bx*ax
- mov cx,NOT dx
-
- Veamos ahora una versión del programa anterior utilizando las directivas de
- segmento simplificadas. Veamos cómo se simplifica mucho:
-
- =================8<============================8<===========================
- .MODEL SMALL ; Un segmento de código, otro con los datos y la pila
- .STACK 200h ; 200h (512d) bytes de pila
- .DATA ; Abre el segmento de datos
- Msg DB 'Hello, world!$'
-
- .CODE ; Abre el segmento de código, cierra el de datos
- Entrada PROC ; Abre el procedimiento 'Entrada'
- mov ax,@data
- mov ds,ax
- mov dx,OFFSET Msg
- mov ah,9
- int 21h ; Servicio 9: imprimir cadena
- mov ax,4C00h
- int 21h ; Servicio 4Ch: retorno al DOS
- Entrada ENDP ; Cierra el procedimiento 'Entrada'
-
- END Entrada ; Fin del programa (punto de entrada 'Entrada'), cierra
- ; el segmento de código
- =================8<============================8<===========================
-
- Las nuevas directivas utilizadas son las que comienzan con un punto. La
- primera, '.MODEL', especifica el modelo de memoria que se va a utilizar. En
- función de el modelo de memoria se seleccionan los nombres de los segmentos, de
- manera que se pueda linkar el módulo con otro módulo escrito en un lenguaje de
- alto nivel usando el mismo modelo de memoria. La directiva '.STACK' genera un
- segmento de pila, de 1024 bytes en caso de que no se especifique el tamaño. La
- directiva '.DATA' abre el segmento de datos, cerrando el segmento anterior en
- caso de que haya alguno abierto. La directiva '.CODE' abre el segmento de códi-
- go, cerrando (en este caso) el segmento de datos. Por fin, la directiva 'END'
- es ampliada para cerrar el segmento abierto cuando aparece, en este caso el
- segmento de código.
-
- Ya que no sabemos el nombre que da el ensamblador a los distintos segmentos,
- existen algunos símbolos que se definen automáticamente al encontrar la direc-
- tiva '.MODEL'. Estos símbolos representan el nombre de los distintos segmentos
- generados, y son dos: @code y @data. Por eso, en la primera instrucción carga-
- mos DS con @data, para poder acceder a 'Msg', que está en el segmento dado por
- '.DATA'.
-
- El propio ensamblador genera el 'ASSUME...' al encontrar la directiva
- '.CODE'. Como vemos, no es necesario preocuparse por casi nada al usar las
- directivas simplificadas de segmento. De hecho, ya no es habitual encontrar
- programas escritos con las directivas antiguas. Estas directivas se añadieron
- para facilitar el desarrollo de programas con módulos en distintos lenguajes,
- ya que la tarea se complica mucho usando las directivas clásicas.
-
- En el siguiente capítulo veremos el último concepto que nos queda por ver
- de microprocesadores: los flags. Veremos algunas instrucciones nuevas y alguna
- interrupción nueva, viéndolo todo con un pequeño programa de ejemplo. De aquí
- en adelante, iremos cubriendo distintos temas progresivamente, de manera que
- se vean ejemplos de todos los usos habituales del lenguaje ensamblador. De
- esta manera, aunque el curso no será muy útil como referencia de ASM, quien
- lo siga habrá visto la mayoría de los aspectos del ASM y podrá abordar cual-
- quier proyecto en este lenguaje, que es cuando realmente adquirirá verdadero
- dominio del lenguaje. Os recomiendo que os hagáis al menos con dos manuales o
- guías de referencia: una con las instrucciones del 80x86, y otra con las
- interrupciones de la BIOS y el MSDOS. Sobre las instrucciones no me atrevo a
- recomendar ninguna, mientras que sobre las interrupciones tenéis por ahí unos
- documentos, escritos por Ralph Brown, denominados INTERxxA.ARJ, INTERxxB.ARJ e
- INTERxxC.ARJ (xx es la versión, la última es la 33, creo) con la más completa
- referencia de interrupciones que existe (son muchos megas de texto!). Son de
- dominio público, por lo que las encontraréis en bastantes BBS. Ahí tendréis
- todo lo que queráis saber sobre los usos de las diferentes interrupciones.
-
- Salut :-)
-
- Jon